package com.platform.system.service.impl;

import com.platform.system.domain.ProCategory;
import com.platform.system.domain.ProProduct;
import com.platform.system.domain.vo.RatingVo;
import com.platform.system.mapper.ProCategoryMapper;
import com.platform.system.mapper.ProProductMapper;
import com.platform.system.service.IRecommenderService;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;

@Service
public class RecommenderServiceImpl implements IRecommenderService {

    @Autowired
    private ProProductMapper proProductMapper;
    @Autowired
    private ProCategoryMapper proCategoryMapper;

    // 用户 -> 商品 -> 评分
    private Map<Long, Map<Long, Integer>> userRatings = new HashMap<>();

    // 商品相似度矩阵：itemA -> itemB -> similarity score
    private Map<Long, Map<Long, Double>> itemSimilarity = new HashMap<>();

    private Map<Long, Long> productCategoryMap = new HashMap<>();

    // 分类权重系数
    private static final double CATEGORY_WEIGHT = 0.1; // 同类商品相似度额外加分

    @PostConstruct
    public void init() {
        loadAllRatings();            // 加载所有评分数据
        loadProductCategories();
        buildItemSimilarityMatrix(); // 构建商品相似度矩阵
    }

    // 从数据库加载评分数据
    private void loadAllRatings() {
        List<RatingVo> ratings = proProductMapper.getAllRatings();
        for (RatingVo r : ratings) {
            userRatings.computeIfAbsent(r.getUserId(), k -> new HashMap<>())
                    .put(r.getProducproId(), r.getRating());
        }
    }

    // 加载商品分类信息
    private void loadProductCategories() {
        List<ProCategory> proCategories = proCategoryMapper.selectProCategoryListAll(null);
        for (ProCategory p : proCategories) {
            productCategoryMap.put(p.getId(), p.getId());
        }
    }

    // 构建商品之间的余弦相似度
    private void buildItemSimilarityMatrix() {
        // 商品 -> 用户 -> 评分
        Map<Long, Map<Long, Integer>> itemRatings = new HashMap<>();
        for (Map.Entry<Long, Map<Long, Integer>> entry : userRatings.entrySet()) {
            Long userId = entry.getKey();
            for (Map.Entry<Long, Integer> itemEntry : entry.getValue().entrySet()) {
                Long itemId = itemEntry.getKey();
                Integer rating = itemEntry.getValue();
                itemRatings.computeIfAbsent(itemId, k -> new HashMap<>()).put(userId, rating);
            }
        }

        List<Long> items = new ArrayList<>(itemRatings.keySet());

        for (int i = 0; i < items.size(); i++) {
            Long itemA = items.get(i);
            Map<Long, Integer> ratingsA = itemRatings.get(itemA);

            for (int j = i + 1; j < items.size(); j++) {
                Long itemB = items.get(j);
                Map<Long, Integer> ratingsB = itemRatings.get(itemB);

                Set<Long> commonUsers = new HashSet<>(ratingsA.keySet());
                commonUsers.retainAll(ratingsB.keySet());

                if (commonUsers.isEmpty()) {
                    continue;
                }

                double dotProduct = 0, normA = 0, normB = 0;
                for (Long userId : commonUsers) {
                    double ratingA = ratingsA.get(userId);
                    double ratingB = ratingsB.get(userId);
                    dotProduct += ratingA * ratingB;
                    normA += ratingA * ratingA;
                    normB += ratingB * ratingB;
                }

                // 检查是否会出现除以零的情况
                if (normA == 0 || normB == 0) {
                    continue;
                }

                double sim = dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));

                // 如果两个商品属于同一类目，则增加相似度权重
                Long categoryA = productCategoryMap.get(itemA);
                Long categoryB = productCategoryMap.get(itemB);
                if (categoryA != null && categoryB != null && categoryA.equals(categoryB)) {
                    sim += CATEGORY_WEIGHT;
                }

                // 更新相似度矩阵
                Map<Long, Double> similarityMapA = itemSimilarity.computeIfAbsent(itemA, k -> new HashMap<>());
                Map<Long, Double> similarityMapB = itemSimilarity.computeIfAbsent(itemB, k -> new HashMap<>());
                similarityMapA.put(itemB, sim);
                similarityMapB.put(itemA, sim);
            }
        }
    }

    // 为用户推荐商品
    public List<ProProduct> recommendItems(Long userId, int topN) {
        Map<Long, Integer> userRating = userRatings.get(userId);
        if (userRating == null || userRating.isEmpty()) return Collections.emptyList();

        for (Long aLong : itemSimilarity.keySet()) {
            System.out.println(aLong +"__"+ itemSimilarity.get(aLong));
        }

        Map<Long, Double> recommendationScores = new HashMap<>();

        for (Map.Entry<Long, Integer> ratedItem : userRating.entrySet()) {
            Long itemId = ratedItem.getKey();
            double rating = ratedItem.getValue();
            Map<Long, Double> similarItems = itemSimilarity.getOrDefault(itemId, Collections.emptyMap());

            for (Map.Entry<Long, Double> simEntry : similarItems.entrySet()) {
                Long recommendedItemId = simEntry.getKey();
                if (userRating.containsKey(recommendedItemId)) continue;

                double weight = simEntry.getValue() * rating;
                recommendationScores.merge(recommendedItemId, weight, Double::sum);
            }
        }

        List<Long> recommendedItemIds = recommendationScores.entrySet()
                .stream()
                .sorted(Map.Entry.<Long, Double>comparingByValue().reversed())
                .limit(topN)
                .map(Map.Entry::getKey)
                .toList();

        if (recommendedItemIds.isEmpty()) return Collections.emptyList();

        Long[] ids = recommendedItemIds.stream().toArray(Long[]::new);;

        return proProductMapper.getProductsByIds(ids);
    }
}